/*
 * Copyright (c) 2024 iSOFT INFRASTRUCTURE SOFTWARE CO., LTD.
 * easyAda is licensed under Mulan PubL v2.
 * You can use this software according to the terms and conditions of the Mulan PubL v2.
 * You may obtain a copy of Mulan PubL v2 at:
 *          http://license.coscl.org.cn/MulanPubL-2.0
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PubL v2 for more details.
 */

#ifndef STRING_H
#define STRING_H

#include <stdlib/assert.h>
#include <stdlib/stddef.h>

static inline void *memset(void *s, int c, size_t n)
{
#ifdef CONFIG_MEMSET_OPTSPEED
    uintptr_t addr  = (uintptr_t)s;
    uint16_t  val16 = ((uint16_t)c << 8) | (uint16_t)c;
    uint32_t  val32 = ((uint32_t)val16 << 16) | (uint32_t)val16;
#ifdef CONFIG_MEMSET_64BIT
    uint64_t val64 = ((uint64_t)val32 << 32) | (uint64_t)val32;
#endif
    if (n > 0) {
        if ((addr & 1) != 0) {
            *(uint8_t *)addr = (uint8_t)c;
            addr += 1;
            n -= 1;
        }
        if (n >= 2) {
            if ((addr & 3) != 0) {
                *(uint16_t *)addr = val16;
                addr += 2;
                n -= 2;
            }
#ifndef CONFIG_MEMSET_64BIT
            while (n >= 4) {
                *(uint32_t *)addr = val32;
                addr += 4;
                n -= 4;
            }
#else
            if (n >= 4) {
                if ((addr & 7) != 0) {
                    *(uint32_t *)addr = val32;
                    addr += 4;
                    n -= 4;
                }
                while (n >= 8) {
                    *(uint64_t *)addr = val64;
                    addr += 8;
                    n -= 8;
                }
            }
#endif
        }

#ifdef CONFIG_MEMSET_64BIT
        if (n >= 4) {
            *(uint32_t *)addr = val32;
            addr += 4;
            n -= 4;
        }
#endif
        if (n >= 2) {
            *(uint16_t *)addr = val16;
            addr += 2;
            n -= 2;
        }

        if (n >= 1) {
            *(uint8_t *)addr = (uint8_t)c;
        }
    }
#else
    unsigned char *p = (unsigned char *)s;
    while (n-- > 0U) {
        *p++ = c;
    }
#endif
    return s;
}

static inline void *memcpy(void *dest, const void *src, size_t n)
{
    unsigned char *pout = (unsigned char *)dest;
    unsigned char *pin  = (unsigned char *)src;
    while (n-- > 0U) {
        *pout++ = *pin++;
    }
    return dest;
}

static inline int memcmp(const void *cs, const void *ct, size_t count)
{
    const unsigned char *su1, *su2;
    int                  res = 0;

    for (su1 = cs, su2 = ct; 0U < count; ++su1, ++su2, count--) {
        if ((res = *su1 - *su2) != 0) {
            break;
        }
    }
    return res;
}

static inline char *strcat(char *dest, const char *src)
{
    char *tmp = dest;

    while (*dest != '\0') {
        dest++;
    }
    while ((*dest++ = *src++) != '\0') {
        ;
    }
    return tmp;
}

static inline char *strncat(char *dest, const char *src, size_t count)
{
    char *tmp = dest;

    if (count != 0U) {
        while (*dest != '\0') {
            dest++;
        }
        while ((*dest++ = *src++) != 0) {
            if (--count == 0) {
                *dest = '\0';
                break;
            }
        }
    }
    return tmp;
}

static inline int strncmp(const char *cs, const char *ct, size_t nb)
{
    int result = 0;
    for (; nb > 0U; nb--) {
        if ((result = (int)*cs - (int)*ct++) != 0 || !*cs++) {
            break;
        }
    }

    return result;
}

static inline size_t strlen(const char *s)
{
    const char *sc;
    for (sc = s; *sc != '\0'; ++sc) {
        ;
    }
    return sc - s;
}

static inline size_t strnlen(const char *s, size_t count)
{
    const char *sc;
    for (sc = s; count-- && *sc != '\0'; ++sc) {
        ;
    }
    return sc - s;
}

static inline char *strpbrk(const char *cs, const char *ct)
{
    const char *sc1, *sc2;

    for (sc1 = cs; *sc1 != '\0'; ++sc1) {
        for (sc2 = ct; *sc2 != '\0'; ++sc2) {
            if (*sc1 == *sc2) {
                return (char *)sc1;
            }
        }
    }
    return NULL;
}

static inline int strcmp(const char *cs, const char *ct)
{
    register signed char result;
    for (;;) {
        if ((result = *cs - *ct++) != 0 || !*cs++) {
            break;
        }
    }

    return result;
}

static inline size_t strcpy(char *dst, const char *src)
{
    size_t      result = 0;
    char       *p      = dst;
    const char *q      = src;
    if (dst != 0 && src != 0) {
        while (*q != 0) {
            *p = *q;
            p++;
            q++;
            result++;
        }
    }
    *p++ = '\0';
    return result;
}

static inline size_t strcspn(const char *s, const char *reject)
{
    const char *p;
    const char *r;
    size_t      count = 0;

    for (p = s; *p != '\0'; ++p) {
        for (r = reject; *r != '\0'; ++r) {
            if (*p == *r) {
                return count;
            }
        }
        ++count;
    }
    return count;
}

static inline void *memchr(const void *s, int c, size_t n)
{
    const unsigned char *p = (const unsigned char *)s;

    if (s != NULL) {
        while (n-- != 0U) {
            if (*p == (unsigned char)c) {
                return (void *)p;
            }

            p++;
        }
    }

    return NULL;
}
static inline char *strncpy(char *dest, const char *source, size_t count)
{
    char *start = dest;
    while (count && (*dest++ = *source++)) {
        count--;
    }
    if (count != 0U) {
        while (--count != 0U) {
            *dest++ = '\0';
        }
    }
    return start;
}

static inline void *memmove(void *dest, const void *src, size_t count)
{
    char *tmp;
    char *s;

    if (dest <= src) {
        tmp = (char *)dest;
        s   = (char *)src;

        while (count-- != 0U) {
            *tmp++ = *s++;
        }
    } else {
        tmp = (char *)dest + count;
        s   = (char *)src + count;

        while (count-- != 0U) {
            *--tmp = *--s;
        }
    }

    return dest;
}
static inline char *strchr(const char *s, int c)
{
    if (s != NULL) {
        for (;; s++) {
            if (*s == c) {
                return (char *)s;
            }

            if (!*s) {
                break;
            }
        }
    }

    return NULL;
}

static inline char *strstr(const char *str, const char *substr)
{
    const char *candidate;
    char        ch;
    size_t      len;

    len = strlen(substr);
    ch  = *substr;

    if (!ch) {
        return (char *)str;
    }

    candidate = str;
    for (;;) {
        candidate = strchr(candidate, ch);
        if (!candidate || strlen(candidate) < len) {
            return NULL;
        }

        if (strncmp(candidate, substr, len) == 0) {
            return (char *)candidate;
        }

        candidate++;
    }
    return NULL;
}
static inline char *strrchr(const char *s, int c)
{
    if (s != NULL) {
        const char *p = &s[strlen(s)];
        for (; p >= s; p--) {
            if (*p == c) {
                return (char *)p;
            }
        }
    }

    return NULL;
}

static inline size_t strspn(const char *s, const char *accept)
{
    const char *p;
    const char *a;
    size_t      count = 0;

    for (p = s; *p != '\0'; ++p) {
        for (a = accept; *a != '\0'; ++a) {
            if (*p == *a) {
                break;
            }
        }
        if (*a == '\0') {
            return count;
        }
        ++count;
    }
    return count;
}

static inline char *strtok(char *str, const char *delim)
{
    static char *last = NULL;
    if (str == NULL) {
        str = last;
    }
    if (str == NULL && last == NULL) {
        return NULL;
    }

    char *s = str;
    while (*s != '\0') {
        while (*delim != '\0') {
            if (*s == *delim && *s) {
                last = s + 1;
                if (s - str == '\0') {
                    str = last;
                    break;
                }
                *s = '\0';
                return str;
            }
            if (!*s) {
                last = NULL;
                return str;
            }
            s++;
        }
        s++;
    }
    return NULL;
}

#endif
